/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2017-2019.
 * Description: add cpu param feature
 * Author: xiaowei
 * Create: 2017/10/18
 */

#include <linux/init.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/seq_file.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>

#include <linux/hal/hal_log.h>
#include <linux/hal/cpu_param.h>

#ifdef CONFIG_ACPI
#include <linux/acpi.h>
#endif

#include <asm/cputype.h>

#ifdef CONFIG_ARM
volatile unsigned int slave_boot_addr;
volatile unsigned int slave_boot_times;
/* mpidr[0:7]=aff0, mpidr[8:15]=aff1, mpidr[16:23]=aff2 */
#define aff_level_invalid(level) ((level < 0) || (level > 2))
#endif

#ifdef CONFIG_ARM64
bool die_id_in_cpuinfo;
/* mpidr[32:39]=aff3, inconsecutive with aff0~2, regarded as level 4. */
#define aff_level_invalid(level) (((level < 0) || (level > 2)) && (level != 4))
#endif

int cpu_aff_level;
int cluster_aff_level;
unsigned int cpu_per_cluster;

static int match_str(const char *source, const char *target)
{
	const char *str = source;

	if (!memcmp(str, target, strlen(target)))
		return 1;

	return 0;
}

static void parse_cpu_desc(const char *cpu_desc)
{
	if (match_str(cpu_desc, "6219e")) {
		cpu_type_num = SD6219E_CPU;
		return ;
	}

	cpu_type_num = simple_strtol(cpu_desc, (char **) &cpu_desc, 10);
}

int arch_cpu_param_init(void)
{
	struct device_node *p_node_tmp = NULL;
	const char *cpu_desc = NULL;
	int err = 0;

#ifdef CONFIG_ACPI
	if (!acpi_disabled)
		return 0;
#endif

	p_node_tmp = of_find_node_by_path("/cpu_extra_info");
	if (p_node_tmp == NULL) {
		pr_err(DTS_CPU_ERR "Find cpu_extra_info node fail in arch_cpu_param!\n");
	}

	if (of_property_read_string(p_node_tmp, "cpu_desc", &cpu_desc)) {
		pr_err(DTS_CPU_ERR "Find cpu_desc fail in cpu_extra_info node, we could not get cpu type info.\n");
		cpu_type_num = 0;
		err = -1;
	} else {
		parse_cpu_desc(cpu_desc);
		pr_info("[dts]:cpu type is %s\n", cpu_desc);
	}

#ifdef CONFIG_ARM
	/* arm64 use spin-table or psci for slave core boot, do not need specify these two params in dtsi */
	if (of_property_read_u32(p_node_tmp, "slave_boot_addr", (u32 *)&slave_boot_addr)) {
		pr_warn(DTS_CPU_WARN "Find slave_boot_addr fail, make sure the soc isn't SMP!\n");
		slave_boot_addr = 0x80000000;
		err = -1;
	}

	if (of_property_read_u32(p_node_tmp, "slave_boot_times", (u32 *)&slave_boot_times)) {
		pr_warn(DTS_CPU_WARN "Find slave_boot_times fail, maybe the soc isn't SMP or we reset 1 times as default.\n");
		slave_boot_times = 1;
		err = -1;
	}
#endif

#ifdef CONFIG_ARM64
	die_id_in_cpuinfo = of_property_read_bool(p_node_tmp, "die_id_in_cpuinfo");
#endif

	cpu_aff_level = -1;
	cluster_aff_level = -1;

	if (!of_property_read_s32(p_node_tmp, "cpu_aff_level", (int *)&cpu_aff_level)) {
		if (aff_level_invalid(cpu_aff_level)) {
			pr_err("[slave core boot]: cpu affinity level is incorrectly configured in dts\n");
			cpu_aff_level = -1;
			err = -1;
		}
	}

	if (!of_property_read_s32(p_node_tmp, "cluster_aff_level", (int *)&cluster_aff_level)) {
		if (aff_level_invalid(cluster_aff_level)) {
			pr_err("[slave core boot]: cluster affinity level is incorrectly configured in dts\n");
			cluster_aff_level = -1;
			err = -1;
		}
	}

	if (of_property_read_s32(p_node_tmp, "cpu_per_cluster", (int *)&cpu_per_cluster))
		cpu_per_cluster = 4; // default for hisilicon soc

	return err;
}

